package rolesystem.agentlogic.managers;

import java.util.ArrayList;

import rolesystem.agentlogic.parameters.ConfigurationParameters;
import rolesystem.agentlogic.utilityclasses.CarrierData;
import rolesystem.agentlogic.utilityclasses.SameManagerData;
import rolesystem.core.RoleException;
import rolesystem.core.RoleRegistration;
import rolesystem.core.RoleSystem;
import rolesystem.middle.managers.CommunicationManager;
import rolesystem.middle.managers.DataOutputManager;
import rolesystem.middle.utilityclasses.AgentData;
import rolesystem.middle.utilityclasses.AgentMap;
import rolesystem.middle.utilityclasses.EventData;
import rolesystem.middle.utilityclasses.MessagePayload;
import rolesystem.middle.utilityclasses.PositionSq;
import rolesystem.middle.utilityclasses.Square;
import rolesystem.middle.utilityclasses.Utils;
import rolesystem.roles.carry.Carrier;
import rolesystem.roles.carry.Coordinator;
import rolesystem.roles.carry.Manager;

public class ContactManager 
{
	private int id;
	
	private RoleSystem roleSystem=null;
	
	private CommunicationManager communicationManager;
	
	static
	{
	}
	
	public ContactManager(int id,CommunicationManager communicationManager)
	{
		this.id=id;	
		this.communicationManager=communicationManager;
	}
	
	public void setRoleSystem(RoleSystem roleSystem)
	{
		this.roleSystem=roleSystem;	
	}
	
	/**
	 * Put the agent to sleep for a number of milliseconds. 
	 * 
	 * @param sleepTime Number of ms that the agent will sleep 
	 * 
	 */	
	private void sleepAgent(int sleepTime) 
	{	
		try
		{
			Thread.sleep(sleepTime);
		} catch(InterruptedException ie) {}
	}
	
	
	/**
	 * Check if a manager already manages the object. 
	 * 
	 * @param  registration a reference to the role assigned to the agents, necessary to communicate with others 
	 * @return a class containing the result of the check
	 * @see SameManagerData
	 */	
	public boolean CheckForSameManager(RoleRegistration registration, PositionSq pos) throws RoleException
	{
		communicationManager.receiveEventTimeout(registration,ConfigurationParameters.REPTO_MANAGER);
		
		EventData data=communicationManager.requestEvent(Carrier.KE_iAmManagerHere);
		if(data!=null && data.getContent().getPosition().equalTo(pos))// If "wait mode" isn't active, notify the conflict
		{
			DataOutputManager.printString(id +" Other Manager - Same position");
			return true;
		}
		
		DataOutputManager.printString(id +" No manager is managing this object.");
		return false;
	}
	
	/**
	 * Send a message to the previously contacted agents telling them to come to the agent position, 
	 * then waits their arrival. 
	 * 
	 * @param  registration a reference to the role assigned to the agents, necessary to communicate with others 
	 * @param  weight the number of agents necessary to carry the object
	 * @param  goal the agent position
	 * @param  risposte_carrier the data necessary to communicate with the previously contacted agents
	 */	
	public ArrayList<CarrierData> waitForCarriers(RoleRegistration registration, int weight, PositionSq goal) throws RoleException
	{
		ArrayList<CarrierData> risposte_carrier= new ArrayList<CarrierData>();
		
		DataOutputManager.printString(id +" My position sent. Waiting for slave arrival");
		
		int n_risposte=0;
		while(true)
		{
			communicationManager.receiveEvent(registration);
			
			EventData data= communicationManager.requestEvent(Manager.KE_agentReached);
			if(data!=null)
			{	
				n_risposte++;
				risposte_carrier.add(new CarrierData(data.getSender()));
			}
				
			if(n_risposte==(weight-1))
				break;		
		}
		
		return risposte_carrier;
		
	}
	
	
	public void collaborativeCarryMessages(RoleRegistration registration,  ArrayList<CarrierData> risposte_carrier,PositionSq actPosition) throws RoleException
	{
		for(int i=0;i<risposte_carrier.size();i++)
			registration.doAction(Manager.moveOneSquare(risposte_carrier.get(i).getAgentID(), new MessagePayload(-1,actPosition)));
		
		for(int n_risposte=0; n_risposte==risposte_carrier.size();)
		{
			communicationManager.receiveEvent(registration);
			
			if(communicationManager.requestEventExist(Manager.KE_moveAck))
				n_risposte++;
		}	
	}
	
	public boolean checkMovementInterrupt(RoleRegistration registration) throws RoleException
	{			
		communicationManager.receiveEvent(registration);
		
		if(communicationManager.checkEventExist(Carrier.KE_youHelpAtPosition)) // If i received a help request, communicate and reach the sender 
			return true;
		
		return false;
	}
	
	/**
	 * Communicate to the carrier agents that this agents is about becoming a manager for a particular object. 
	 * 
	 * @param  registration Reference to the role of the agent in the Role System, necessary to communicate with other agents. 
	 */	
	public void ManagerCheckResponse(RoleRegistration registration,PositionSq check_position) throws RoleException
	{		
		int[] carrierArray = roleSystem.searchForRoleNoWait(Carrier.ROLE_ID);
		
		for(int i=0;i<carrierArray.length;i++)					
			registration.doAction(Manager.iAmManagerHere(carrierArray[i],new MessagePayload(-1,check_position)));
	}
	
	public void contactCoordinator(RoleRegistration registration, int weight, PositionSq goal, PositionSq position) throws RoleException
	{		
		// Contatto i carrier comunicandogli la mia posizione
		int[] coordinatorArray = roleSystem.searchForRole(Coordinator.ROLE_ID);
		
		registration.doAction(Manager.requestHelp(coordinatorArray[0],new MessagePayload(id,position,new Square(weight) )));		
	}
	
	
	public void DecideManager(RoleRegistration registration) throws RoleException
	{
		int[] carrierArray = roleSystem.searchForRoleNoWait(Carrier.ROLE_ID);
		
		int randomNumber= ((int)(Math.random()*(carrierArray.length-1)))+1;
		
		registration.doAction(Carrier.youAreMaster(carrierArray[randomNumber]));	

		ArrayList<Integer> carrierRemove=Utils.arraytoIntegerArrayList(carrierArray);
		
		carrierRemove.remove(new Integer(carrierArray[randomNumber]));

		for(int i=0;i<carrierRemove.size();i++)
			registration.doAction(Carrier.youAreCarrier((int)carrierRemove.get(i)));
	}
	
	public boolean ReceiveManagerAnnounce(RoleRegistration registration) throws RoleException
	{			
		communicationManager.receiveEventBlocking(registration);
		
		if(communicationManager.requestEventExist(Carrier.KE_youAreMaster))
		{			
			DataOutputManager.printString(id + " I'm the coordinator");
			return true;
		}
		if(communicationManager.requestEventExist(Carrier.KE_youAreCarrier))
		{			
			DataOutputManager.printString(id + " I'm a carrier");
			return false;
		}	
		
		DataOutputManager.printString(id + " ERROR in receiving master announce");
		return false;	
	}
	
	public void checkHelpRequestAndAnswer(RoleRegistration registration) throws RoleException
	{			
		communicationManager.receiveEvent(registration);
		sleepAgent(100);
		
		EventData data=communicationManager.requestEvent(Coordinator.KE_requestHelp);
		if(data!=null)
		{	
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_CARRY_COOR);
			
			DataOutputManager.printString(id + " Help request from ID: " + data.getContent().getAgentID() + " JADE ID: "+ data.getSender());
			
			PositionSq found_position= data.getContent().getPosition();
			int foundWeight= data.getContent().getSquare().getWeight();
			
			ArrayList<AgentData> agentsToContact=new ArrayList<AgentData>();
			while(agentsToContact.size()!=(foundWeight-1))
			{	
				agentsToContact= AgentMap.returnNearestAgents(found_position,foundWeight-1,data.getSender());
			}
			
			for(int index=0;index<agentsToContact.size();index++)
			{
				DataOutputManager.printString(id + " CRD - From: "+ data.getSender() + " Contacting ID: " + agentsToContact.get(index).id + " - JADE ID: " + agentsToContact.get(index).role_id);
				registration.doAction(Coordinator.youHelpAtPosition(agentsToContact.get(index).role_id,new MessagePayload(data.getContent().getAgentID(),found_position)));
			}
			// FIXME mettere controllo se non ci sono abbastanza agenti o altra gestione
		}
			
	}
	
	
}
